﻿VM = function () {
    this.State = new MethodState(this);
    this.halt = false;
    this.Console = null;
    this.Continuous = true;
};

VM.prototype.ExecuteCommand = function (commandInfo) {
    var command = new Command(this, commandInfo);
    command.Execute();
};

VM.prototype.Execute = function (commandText) {
    var commandInfo = new CommandInfo(commandText);
    this.ExecuteCommand(commandInfo);
};

VM.prototype.LoadProgram = function (commandArray) {
    this.Reset();
    for (var i = 0; i < commandArray.length; i++) {
        this.State.MethodBody.push(commandArray[i]);
    }
    
    this.onIPChanged();    
    this.onMemoryChanged();
    this.onStackChanged();
    this.onActiveChanged();
};

VM.prototype.ExecuteProgram = function () {
    this.executeIFE();
};

VM.prototype.StepOne = function () {
    if (this.halt)
        return;
    
    if (this.IsEndOfMethod())
        return ;
    
    var currentCommand = this.State.MethodBody[this.State.IP];
    this.IncrementIP();
    this.Execute(currentCommand);

    if (this.IsEndOfMethod())
        this.SetHalt(true);
};

VM.prototype.Return = function ()
{
    this.Pause();
};

VM.prototype.Reset = function () {
    this.State = new MethodState(this);
    this.SetHalt(false);
};

VM.prototype.Pause = function () {
    this.SetHalt(true);
};

VM.prototype.Resume = function () {
    this.SetHalt(false);
    if (this.Continuous)
        this.executeIFE();
};

VM.prototype.executeIFE = function () {
    while (!this.halt) {
        this.StepOne();

        if (this.IsEndOfMethod())
            this.SetHalt(true);
    }
};

VM.prototype.SetHalt = function (newHaltValue)
{
    this.halt = newHaltValue;
    this.onActiveChanged();
};

VM.prototype.Branch = function (offset) {
    var testIp = this.State.IP + offset;

    if (testIp < 0 || testIp > this.State.MethodBody.length - 1)
        throw "InvalidBranchOperation";

    this.SetIP(this.State.IP + offset);
};

VM.prototype.GetIP = function () {
    if (this.State.IP < 0 || this.IsEndOfMethod())
        return null;
    
    return this.State.IP;
};

VM.prototype.SetIP = function (newIp) {
    this.State.IP = newIp;
    this.onIPChanged();
};

VM.prototype.IsEndOfMethod = function ()
{
    return this.State.IP >= this.State.MethodBody.length;  
};

VM.prototype.IncrementIP = function () {
    this.SetIP(this.State.IP + 1);
};

VM.prototype.CurrentInstruction = function () {
    if (this.IsEndOfMethod())
        return null;
    
    return this.State.MethodBody[this.State.IP];
};

VM.prototype.IsActive = function ()
{
    return !this.halt;    
};

VM.prototype.SetEventHandler = function (eventHandler)
{
    this.EventHandler = eventHandler;
};

VM.prototype.onEvent = function (eventName)
{
    if (this.EventHandler && this.EventHandler.PropertyChanged)
        this.EventHandler.PropertyChanged(eventName);    
};


VM.prototype.onActiveChanged = function () {
    this.onEvent('ActiveChanged');
};

VM.prototype.onStackChanged = function () {
    this.onEvent('StackChanged');
};

VM.prototype.onMemoryChanged = function () {
    this.onEvent('MemoryChanged');
};

VM.prototype.onIPChanged = function () {
    this.onEvent('IPChanged');
};
